#ifndef ASM_SYSCALL_H
#define ASM_SYSCALL_H

#include <seminix/linkage.h>
#include <asm/ptrace.h>

#define SC_ARM64_REGS_TO_ARGS(x, ...)				\
    __MAP(x,__SC_ARGS					\
          ,,regs->regs[0],,regs->regs[1],,regs->regs[2]	\
          ,,regs->regs[3],,regs->regs[4],,regs->regs[5])

#define __SYSCALL_DEFINEx(x, name, ...)						\
    asmlinkage long __arm64_sys##name(const struct pt_regs *regs);		\
    static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));		\
    static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));	\
    asmlinkage long __arm64_sys##name(const struct pt_regs *regs)		\
    {									\
        return __se_sys##name(SC_ARM64_REGS_TO_ARGS(x,__VA_ARGS__));	\
    }									\
    static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))		\
    {									\
        long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));	\
        __MAP(x,__SC_TEST,__VA_ARGS__);					\
        __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));		\
        return ret;							\
    }									\
    static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

#ifndef SYSCALL_DEFINE0
#define SYSCALL_DEFINE0(sname)					\
    asmlinkage long __arm64_sys_##sname(void);		\
    asmlinkage long __arm64_sys_##sname(void)
#endif

typedef long (*syscall_fn_t)(struct pt_regs *regs);

extern const syscall_fn_t sys_call_table[];

static inline int syscall_get_nr(struct task_struct *task,
                 struct pt_regs *regs)
{
    return regs->syscallno;
}

static inline void syscall_rollback(struct task_struct *task,
                    struct pt_regs *regs)
{
    regs->regs[0] = regs->orig_x0;
}

static inline long syscall_get_error(struct task_struct *task,
                     struct pt_regs *regs)
{
    unsigned long error = regs->regs[0];
    return IS_ERR_VALUE(error) ? error : 0;
}

static inline long syscall_get_return_value(struct task_struct *task,
                        struct pt_regs *regs)
{
    return regs->regs[0];
}

static inline void syscall_set_return_value(struct task_struct *task,
                        struct pt_regs *regs,
                        int error, long val)
{
    regs->regs[0] = (long) error ? error : val;
}

#define SYSCALL_MAX_ARGS 6

static inline void syscall_get_arguments(struct task_struct *task,
                     struct pt_regs *regs,
                     unsigned int i, unsigned int n,
                     unsigned long *args)
{
    if (n == 0)
        return;

    if (i + n > SYSCALL_MAX_ARGS) {
        unsigned long *args_bad = args + SYSCALL_MAX_ARGS - i;
        unsigned int n_bad = n + i - SYSCALL_MAX_ARGS;
        pr_warn("%s called with max args %d, handling only %d\n",
               __func__, i + n, SYSCALL_MAX_ARGS);
        memset(args_bad, 0, n_bad * sizeof(args[0]));
    }

    if (i == 0) {
        args[0] = regs->orig_x0;
        args++;
        i++;
        n--;
    }

    memcpy(args, &regs->regs[i], n * sizeof(args[0]));
}

static inline void syscall_set_arguments(struct task_struct *task,
                     struct pt_regs *regs,
                     unsigned int i, unsigned int n,
                     const unsigned long *args)
{
    if (n == 0)
        return;

    if (i + n > SYSCALL_MAX_ARGS) {
        pr_warn("%s called with max args %d, handling only %d\n",
               __func__, i + n, SYSCALL_MAX_ARGS);
        n = SYSCALL_MAX_ARGS - i;
    }

    if (i == 0) {
        regs->orig_x0 = args[0];
        args++;
        i++;
        n--;
    }

    memcpy(&regs->regs[i], args, n * sizeof(args[0]));
}

#endif /* !ASM_SYSCALL_H */
